package com.rankeiot.platform.controller;

import cn.hutool.core.io.resource.ResourceUtil;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.jfinal.template.Engine;
import com.rankeiot.core.EasyCorePlugin;
import com.rankeiot.core.anno.Auth;
import com.rankeiot.core.anno.OperationLog;
import com.rankeiot.core.config.ConfigManager;
import com.rankeiot.core.data.Item;
import com.rankeiot.core.data.LocalizedMessage;
import com.rankeiot.core.data.response.Resp;
import com.rankeiot.core.event.GetJsConfigEvent;
import com.rankeiot.core.exception.AuthException;
import com.rankeiot.core.util.*;
import com.rankeiot.core.Current;
import com.rankeiot.easy.vue.EasyVueConfiguration;
import com.rankeiot.platform.PlatformMenu;
import com.rankeiot.platform.config.PlatformConfig;
import com.rankeiot.platform.domain.MultiDictionary;
import com.rankeiot.platform.domain.User;
import com.rankeiot.core.data.UserInfo;
import com.rankeiot.platform.service.CacheDataService;
import com.rankeiot.platform.service.ServerEventPushService;
import com.rankeiot.platform.service.UserService;
import com.rankeiot.platform.util.PasswordUtil;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import org.sagacity.sqltoy.SqlToyContext;
import org.sagacity.sqltoy.dao.SqlToyLazyDao;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;


import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


/**
 * 系统接口
 */
@Tag(name="平台接口")
//@Auth
@RestController
@RequestMapping("/system")
@RequiredArgsConstructor
public class SystemController {

    final SqlToyContext sqlToyContext;
    final SqlToyLazyDao dao;
    final UserService userService;
    final CacheDataService cacheDataService;
    final Cache<String,Map> localCache = Caffeine.newBuilder()
            .expireAfterAccess(30, TimeUnit.MINUTES)
            .weakKeys()
            .weakValues()
            .build();

    final Engine engine;

    final ApplicationContext context;
    // 获得多语言对象
    final MessageSource messageSource;

    final ServerEventPushService serverEventPushService;

    @Value("${platform.loginCaptcha:true}")
    boolean loginCaptcha;


    @Auth
    @GetMapping("events")
    public SseEmitter events(String uid){
        if(StringUtil.isEmpty(uid)){
            Resp.fail("error");
        }
        UserInfo userInfo=Current.user();
        return serverEventPushService.subscribe(userInfo.getUsername(),uid);
    }
    //发送在线通知
    @Auth(PlatformMenu.CONSOLE)
    @PostMapping("notification")
    public Resp notification(@RequestBody String body){
        serverEventPushService.sendAll("notification",body);
        return Resp.ok();
    }
    /**
     * 前端配置
     *
     * @return
     */
    @Auth(login = false)
    @GetMapping (value = "/config", produces = "application/javascript")
    public String config(boolean debug) {
        if(!EasyCorePlugin.hasDataSource){
            return "location.href='install.html'";
        }
        Map model=new HashMap();
        model.put("configs", JSONUtil.toJSONString(ConfigManager.getFrontConfigs()));
        model.put("isDev", EasyVueConfiguration.devModel||debug);

//        long time = Long.valueOf(LicenseController.info.get("date"));
//        if (time <= System.currentTimeMillis()) {
//            System.exit(0);
//        }
//        if (time <= System.currentTimeMillis() + LicenseController.WarnTime) {
//            String dateStr = DateUtil.format(new Date(time), "yyyy-MM-dd hh:mm");
//            model.put("expire", "截止 " + dateStr + ",系统授权将到期。到期后系统将无法正常运行！！！");
//        }

      //  JsConfigPoint point = new JsConfigPoint();
        GetJsConfigEvent getJsConfigEvent=new GetJsConfigEvent();
       // ExtendsManager.invoke(point);
        context.publishEvent(getJsConfigEvent);

        model.put("extend", getJsConfigEvent.getConfigJs());
        // mv.put("expireSoon",true);//LicenseController.expireSoon);
        //无法获取登录用户相关的数据
        // mv.put("userInfo",this.userInfo(ctx).getData());
       // mv.setViewName("config.js");
        engine.setDevMode(EasyVueConfiguration.devModel);
        return  engine.getTemplate("config.js").renderToString(model);
    }
    @Auth(login = false)
    @PostMapping (value = "/i18n")
    public Resp getI18nMessages(){
       Locale locale = LocaleContextHolder.getLocale();
       Map ret=  localCache.get(locale.getLanguage(),k->{
          // System.out.println(k);
            Map<Object,Object> messages=new HashMap<>();
              for(URL url:ResourceUtil.getResources("i18n/messages.properties")){
                  try(InputStream inputStream = url.openStream()){
                      Properties properties = new Properties();
                      properties.load(inputStream);
                      messages.putAll(properties);
                  } catch (IOException e) {
                      throw new RuntimeException(e);
                  }
              }
           for(URL url:ResourceUtil.getResources("i18n/messages_"+k+".properties")){
               try(InputStream inputStream = url.openStream()){
                   Properties properties = new Properties();
                   properties.load(inputStream);
                   messages.putAll(properties);
               } catch (IOException e) {
                   throw new RuntimeException(e);
               }
           }
            return  messages;
        });
        return Resp.of(ret);
    }
    @Auth(login = false)
    @Operation(summary = "从translate中获取字典")
    @RequestMapping("translate/{cache}")
    public Resp<Map> translate(String cache, String type) {
//        if(type==null){
//            return Result.succeed(new HashMap());
//        }
        HashMap<String, Object[]> cacheData = sqlToyContext.getTranslateManager().getCacheData(cache, type);
        Map result = new HashMap();
        if (cacheData != null) {
            result = cacheData.entrySet().stream()
                    .collect(Collectors.toMap(
                            key -> key.getKey(),
                            value -> value.getValue()[1]));
        }
        return Resp.of(result);
    }

    @Auth(login = false)
    @Operation(summary = "获取字典")
    @GetMapping("dict")
    public Resp allDict(String type) {
        if (StringUtil.isNotEmpty(type)) {
            return Resp.of(cacheDataService.allCodes().get(type));
        }
        return Resp.of(cacheDataService.allCodes());
    }
    private List<Item> decodeDictItems(List<Item> items,Locale locale){
        List<Item> result=new ArrayList<>();
        for(Item item:items){
            result.add(new Item(item.getKey(),LocaleUtil.d(item.getTitle(),locale)));
        }
        return result;
    }
    @Auth(login = false)
    @Operation(summary = "获取字典")
    @PostMapping("dicts")
    public Resp dicts(@RequestBody Set<String> types) {
        Locale locale=LocaleUtil.getLocale();
        if (types!=null&&!types.isEmpty()) {
            Map<String,List> dictMap=new HashMap<>();
            for(String type:types){
                List<Item> items=cacheDataService.allCodes().get(type);
                if(items!=null){
                    dictMap.put(type,decodeDictItems(items,locale));
                }
            }
            return Resp.of(dictMap);
        }
        return Resp.of(new HashMap());
    }
    @Auth(login = false)
    @Operation(summary = "获取多级字典")
    @GetMapping("multiDict")
    public Resp multiDict(@Parameter(description = "字典类型") String type, @Parameter(description = "获取层级") Integer rank) {
        if(rank==null||rank<=1){
            List dicts= dao.query()
                    .sql("select * from t_multi_dictionary where parent_sn = ?")
                    .values(type)
                    .resultType(MultiDictionary.class)
                    .find();
            return Resp.of(dicts);
        }else{
            List dicts= dao.query()
                    .sql("select * from t_multi_dictionary where parent_sn like ? and rank < ?")
                    .values(type,type.length()/2+rank+1)
                    .resultType(MultiDictionary.class)
                    .find();
            return Resp.of(dicts);
        }
    }
    //  @Auth(login = false)
    @Operation(summary = "保持在线")
    @GetMapping("keep")
    public Resp keepOnline() {
        UserInfo user = Current.user();
        if (user != null) {
            Map<String,String> ret=new HashMap<>();
            ret.put("username", user.getUsername());
            ret.put("token",user.getToken());
            return Resp.of(ret);
        } else {
            //自动尝试自动登录
        }
        return Resp.ok();
    }


    @Operation(summary = "用户登录")
    @Auth(login = false)
    @OperationLog("用户登录")
    @PostMapping(value="login",consumes = "application/x-www-form-urlencoded")
    public Resp login(String username, String password, String verify, String cpId, String new_password, @CookieValue(value=Captcha.captchaCookieName,required = false) String vid) {
        if (loginCaptcha) {
            if (StringUtil.isNotEmpty(cpId)) {
                if (!Captcha.verify(verify, cpId)) {
                    Resp.fail(Messages.CaptchaError);
                }
            } else if (!Captcha.verify((ServletRequestAttributes) RequestContextHolder.getRequestAttributes(),verify,vid)) {
                Resp.fail(Messages.CaptchaError);
            }
        }
        if (StringUtil.isEmpty(password)||StringUtil.isEmpty(username)) {
            Resp.fail(Messages.LoginError);
        }
        if(StringUtil.isNotEmpty(new_password)){
            PasswordUtil.checkWeakPassword(new_password);
        }
        if(StringUtil.isNotEmpty(verify)){
            try {
                password= RsaUtil.AESDecrypt(password,verify);
            }catch (Exception e){
                Resp.fail(Messages.UnknownError);
            }
        }
        UserInfo userInfo = userService.loadUserInfo(username, password);
        if (userInfo != null) {
            if(StringUtil.isNotEmpty(new_password)){
                //更新密码
                userInfo.setPassword(PasswordUtil.encodePassword(new_password,userInfo.getUsername()));
                dao.executeSql("update t_user set password=:password where username=:username", userInfo);
            }else{
                //检查密码
                if(PasswordUtil.isWeakPassword(password)){
                    Resp.fail(Messages.PasswordNotSafe,5000);
                }
            }
            Current.login(userInfo);
        } else {
            Resp.fail(Messages.LoginError);
        }
        return Resp.map()
                .set("token", userInfo.getToken())
                .set("un",userInfo.getUserType()+userInfo.getUsername())
                .set("uk", userInfo.getPassword() + TokenUtil.getGlobalSalt());
      //  return Resp.ok();
    }


    @Operation(summary = "获取系统信息")
    @Auth(login = false)
    @GetMapping(value = "app_info")
    public Resp appInfo() {
        Map<String, Object> info = new HashMap<>();
        info.put("appName", PlatformConfig.SystemName.value());
        info.put("logo", PlatformConfig.LogoUrl.value());
        return Resp.of(info);
    }

    @Auth(login = false)
    @GetMapping("fontConfig")
    public Resp fontConfig() {
        return Resp.of(ConfigManager.getFrontConfigs());
    }
//    @Mapping(value = "configs", method = MethodType.GET)
//    public Resp configs() {
//        return Resp.of(Config.configMap());
//    }
//
//    @Mapping(value = "updateConfig", method = MethodType.POST)
//    public Resp updateConfig(String code, String value) {
//        Config.saveConfig(code, value);
//        return Resp.ok();
//    }

    @Operation(summary = "获取当前登录用户信息")
    @Auth(login = false)
    @GetMapping(value = "user_info")
    public Resp<UserInfo> userInfo(HttpServletRequest request, HttpServletResponse response) {
        try {
            UserInfo userInfo = Current.user();
            if (userInfo == null) {
                if (null != Current.getUserToken(request)) {
                    response.setHeader("rtk", "true");
                }
                return Resp.ok();
            }
            return Resp.of(userInfo);
        }catch (AuthException e){
            return Resp.ok();
        }
    }
    @Operation(summary = "保持用户信息3天")
    @Auth(login = false)
    @GetMapping(value = "keep_remember")
    public Resp keepRemember() {
        UserInfo userInfo = Current.user();
        if(userInfo!=null){
            //记住3天
            Current.login(userInfo,3*24*60*60);
        }
        return Resp.ok();
    }
    /**
     * 用户登出
     *
     * @return
     */
    @Operation(summary = "登出当前用户")
    @Auth(login = false)
    @RequestMapping("logout")
    public Resp logout() {
        Current.logout();
        return Resp.ok();
    }
    //http://localhost:9095/system/changePassword

    /**
     * 修改密码
     *
     * @param OldPassword
     * @param ConfirmPassword
     * @param Password
     * @return
     */
    @Operation(summary = "修改用户密码")
    @PostMapping("changePassword")
    public Resp changePassword(String OldPassword, String ConfirmPassword, String Password) {
        if (!Objects.equals(ConfirmPassword, Password)) {
            Resp.fail(Messages.PasswordInconsistent);
        }
        if (StringUtil.isEmpty(Password)) {
            Resp.fail("密码不能为空");
        }
        //检测弱密码
        PasswordUtil.checkWeakPassword(Password);

        UserInfo userInfo = Current.user();
        if (!Objects.equals(userInfo.getPassword(), StringUtil.md5Hex(userInfo.getUsername() + "$" + OldPassword))) {
            Resp.fail("原密码不正确");
        }
        if (Objects.equals(OldPassword, Password)) {
            Resp.fail("新密码和源码相同");
        }
        User u = new User();
        u.setId(Long.valueOf(userInfo.getId()));
        u.setPassword(StringUtil.md5Hex(userInfo.getUsername() + "$" + Password));
        dao.update(u);
        userInfo.setPassword(u.getPassword());
        Resp ret=Resp.map()
                .set("uk", userInfo.getPassword() + TokenUtil.getGlobalSalt());
        ret.setMessage("密码已修改");
        return ret;
    }

    public String e(String str, int k) {
        StringBuilder result = new StringBuilder();
        for (char c : str.toCharArray()) {
            result.append(e(c, k));
        }
        return result.toString();
    }

    private char e(char c, int k) {
        int c1 = c + k % Character.MAX_VALUE;
        if (c1 > Character.MAX_VALUE) {
            return (char) (c1 - Character.MAX_VALUE);
        } else if (c1 < 0) {
            return (char) (c1 + Character.MAX_VALUE);
        } else {
            return (char) c1;
        }
    }
    @AllArgsConstructor
    @Getter
    enum Messages implements LocalizedMessage {
        CaptchaError("captcha error","验证码错误"),
        CodeError("code error","短信验证码错误"),
        LoginError("username or password error","用户名或密码错误"),
        PasswordNotSafe("the current password is not secure, please change the password","当前密码不安全，请修改密码"),
        PasswordInconsistent("two passwords are inconsistent","两次密码不一致"),
        UnknownError("unknown error","未知错误");

        private final String en;
        private final String zh;
    }
}
